// Contains code copied from istanbul-lib-report (https://github.com/istanbuljs/istanbuljs/tree/main/packages/istanbul-lib-report).
// The link to the original license is in the VENDORED.md file in the parent directory.

"use strict";
/*
 Copyright 2012-2015, Yahoo Inc.
 Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms.
 */
const INDENT = "  ";

function attrString(attrs) {
  return Object.entries(attrs || {})
    .map(([k, v]) => ` ${k}="${v}"`)
    .join("");
}

/**
 * a utility class to produce well-formed, indented XML
 * @param {ContentWriter} contentWriter the content writer that this utility wraps
 * @constructor
 */
class XMLWriter {
  constructor(contentWriter) {
    this.cw = contentWriter;
    this.stack = [];
  }

  indent(str) {
    return this.stack.map(() => INDENT).join("") + str;
  }

  /**
   * writes the opening XML tag with the supplied attributes
   * @param {String} name tag name
   * @param {Object} [attrs=null] attrs attributes for the tag
   */
  openTag(name, attrs) {
    const str = this.indent(`<${name + attrString(attrs)}>`);
    this.cw.println(str);
    this.stack.push(name);
  }

  /**
   * closes an open XML tag.
   * @param {String} name - tag name to close. This must match the writer's
   *  notion of the tag that is currently open.
   */
  closeTag(name) {
    if (this.stack.length === 0) {
      throw new Error(`Attempt to close tag ${name} when not opened`);
    }
    const stashed = this.stack.pop();
    const str = `</${name}>`;

    if (stashed !== name) {
      throw new Error(
        `Attempt to close tag ${name} when ${stashed} was the one open`,
      );
    }
    this.cw.println(this.indent(str));
  }

  /**
   * writes a tag and its value opening and closing it at the same time
   * @param {String} name tag name
   * @param {Object} [attrs=null] attrs tag attributes
   * @param {String} [content=null] content optional tag content
   */
  inlineTag(name, attrs, content) {
    let str = "<" + name + attrString(attrs);
    if (content) {
      str += `>${content}</${name}>`;
    } else {
      str += "/>";
    }
    str = this.indent(str);
    this.cw.println(str);
  }

  /**
   * closes all open tags and ends the document
   */
  closeAll() {
    this.stack
      .slice()
      .reverse()
      .forEach((name) => {
        this.closeTag(name);
      });
  }
}

module.exports = XMLWriter;
